package com.nightonke.boommenu.BoomButtons;

import ohos.app.Context;
import ohos.agp.components.element.Element;
import ohos.agp.components.Text.TruncationMode;
import ohos.agp.text.Font;
import ohos.agp.utils.Color;
import ohos.agp.utils.Rect;
import ohos.agp.utils.TextAlignment;

import com.nightonke.boommenu.Piece.BoomPiece;
import com.nightonke.boommenu.Util;

import java.lang.ref.WeakReference;

/**
 * Created by Weiping Huang at 02:20 on 16/11/18
 * For Personal Open Source
 * Contact me at 2584541288@qq.com or nightonke@outlook.com
 */

@SuppressWarnings({"unchecked", "unused"})
public abstract class BoomButtonBuilder<T> {

    private BoomPiece piece = null;
    private WeakReference<BoomButton> boomButtonWeakReference;

    // Basic
    int index = -1;
    InnerOnBoomButtonClickListener listener;
    OnBMClickListener onBMClickListener;
    boolean rotateImage = true;
    boolean rotateText = true;
    boolean containsSubText = true;

    // piece
    Color pieceColor = null;
    int pieceColorRes = 0;

    // Shadow
    boolean shadowEffect = true;
    int shadowOffsetX = Util.dp2px(0);
    int shadowOffsetY = Util.dp2px(3);
    int shadowRadius = Util.dp2px(8);
    Color shadowColor = new Color(Color.getIntColor("#88757575"));
    int shadowCornerRadius = Util.dp2px(5);

    // Images
    int normalImageRes = 0;
    int highlightedImageRes = 0;
    int unableImageRes = 0;
    Element normalImageDrawable = null;
    Element highlightedImageDrawable = null;
    Element unableImageDrawable = null;
    Rect imageRect = new Rect(Util.dp2px(10), Util.dp2px(10), Util.dp2px(70), Util.dp2px(70));
    Rect imagePadding = new Rect(0, 0, 0, 0);

    // Text
    int normalTextRes = 0;
    int highlightedTextRes = 0;
    int unableTextRes = 0;
    String normalText;
    String highlightedText;
    String unableText;
    Color normalTextColor = Color.WHITE;
    int normalTextColorRes = 0;
    Color highlightedTextColor = Color.WHITE;
    int highlightedTextColorRes = 0;
    Color unableTextColor = Color.WHITE;
    int unableTextColorRes = 0;
    Rect textRect = new Rect(Util.dp2px(15), Util.dp2px(52), Util.dp2px(65), Util.dp2px(72));
    Rect textPadding = new Rect(0, 0, 0, 0);
    Font typeface = null;
    int maxLines = 1;
    int textGravity = TextAlignment.CENTER;
    TruncationMode ellipsize = TruncationMode.AUTO_SCROLLING;
    int textSize = 10;

    // Sub text
    int subNormalTextRes = 0;
    int subHighlightedTextRes = 0;
    int subUnableTextRes = 0;
    String subNormalText;
    String subHighlightedText;
    String subUnableText;
    Color subNormalTextColor = Color.WHITE;
    int subNormalTextColorRes = 0;
    Color subHighlightedTextColor = Color.WHITE;
    int subHighlightedTextColorRes = 0;
    Color subUnableTextColor = Color.WHITE;
    int subUnableTextColorRes = 0;
    Rect subTextRect = new Rect(Util.dp2px(70), Util.dp2px(35), Util.dp2px(280), Util.dp2px(55));
    Rect subTextPadding = new Rect(0, 0, 0, 0);
    Font subTypeface = null;
    int subMaxLines = 1;
    int subTextGravity = TextAlignment.START|TextAlignment.VERTICAL_CENTER;
    TruncationMode subEllipsize = TruncationMode.AUTO_SCROLLING;
    int subTextSize = 10;

    // Text for text-outside-circle-button
    int textTopMargin = Util.dp2px(5);
    int textWidth = Util.dp2px(80);
    int textHeight = Util.dp2px(20);

    // Button
    boolean rippleEffect = false;
    Color normalColor = Util.getColor();
    int normalColorRes = 0;
    Color highlightedColor = Util.getColor();
    int highlightedColorRes = 0;
    Color unableColor = Util.getColor();
    int unableColorRes = 0;
    boolean unable = false;
    int buttonRadius = Util.dp2px(40);
    int buttonWidth = Util.dp2px(300);
    int buttonHeight = Util.dp2px(60);
    int buttonCornerRadius = Util.dp2px(5);
    boolean isRound = true;  // only for simple circle/text inside/text outside circle button

    public abstract BoomButton build(Context context);

    /**
     * Get the piece color, only used in BMB package.
     *
     * @param context context
     * @return color of piece
     */
    public Color pieceColor(Context context) {
        if (pieceColor == null && pieceColorRes == 0)
            if (unable) return Util.getColor(context, unableColorRes, unableColor);
            else return Util.getColor(context, normalColorRes, normalColor);
        else if (pieceColor == null) return Util.getColor(context, pieceColorRes);
        else return Util.getColor(context, pieceColorRes, pieceColor);
    }

    /**
     * setUnable
     * @param unables
     */
    public void setUnable(boolean unables) {
        this.unable = unables;
    }

    /**
     * button
     * @return
     */
    public BoomButton button() {
        if (boomButtonWeakReference != null) return boomButtonWeakReference.get();
        return null;
    }

    /**
     * weakReferenceButton
     * @param button
     * @return
     */
    BoomButton weakReferenceButton(BoomButton button) {
        boomButtonWeakReference = new WeakReference<>(button);
        return button;
    }

    /**
     * Set the piece of the builder, only used in BMB package.
     *
     * @param pieceIn the piece
     */
    public void piece(BoomPiece pieceIn) {
        this.piece = pieceIn;
    }

    /**
     * Set the index of the boom-button, only used in BMB package.
     *
     * @param indexIn the index
     * @return the builder
     */
    public BoomButtonBuilder index(int indexIn) {
        this.index = indexIn;
        return this;
    }

    /**
     * Set the listener of the boom-button, only used in BMB package.
     *
     * @param listenerIn the listener
     * @return the builder
     */
    public BoomButtonBuilder innerListener(InnerOnBoomButtonClickListener listenerIn) {
        this.listener = listenerIn;
        return this;
    }

    /**
     * Set listener for when the boom-button is clicked.
     *
     * @param onBMClickListenerIn OnBMClickListener
     * @return the builder
     */
    public T listener(OnBMClickListener onBMClickListenerIn) {
        this.onBMClickListener = onBMClickListenerIn;
        return (T) this;
    }

    /**
     * Whether the image-view should rotate.
     *
     * @param rotateImageIn rotate or not
     * @return the builder
     */
    public T rotateImage(boolean rotateImageIn) {
        this.rotateImage = rotateImageIn;
        return (T) this;
    }

    /**
     * Whether the boom-button should have a shadow effect.
     *
     * @param shadowEffectIn have shadow effect or not
     * @return the builder
     */
    public T shadowEffect(boolean shadowEffectIn) {
        this.shadowEffect = shadowEffectIn;
        return (T) this;
    }

    /**
     * Set the horizontal shadow-offset of the boom-button.
     *
     * @param shadowOffsetXIn the shadow offset x
     * @return the builder
     */
    public T shadowOffsetX(int shadowOffsetXIn) {
        this.shadowOffsetX = shadowOffsetXIn;
        return (T) this;
    }

    /**
     * Set the vertical shadow-offset of the boom-button.
     *
     * @param shadowOffsetYIn the shadow offset y
     * @return the builder
     */
    public T shadowOffsetY(int shadowOffsetYIn) {
        this.shadowOffsetY = shadowOffsetYIn;
        return (T) this;
    }

    /**
     * Set the radius of shadow of the boom-button.
     *
     * @param shadowRadiusIn the shadow radius
     * @return the builder
     */
    public T shadowRadius(int shadowRadiusIn) {
        this.shadowRadius = shadowRadiusIn;
        return (T) this;
    }

    /**
     * Set the corner-radius of the shadow.
     *
     * @param shadowCornerRadiusIn corner-radius of the shadow
     * @return the builder
     */
    public T shadowCornerRadius(int shadowCornerRadiusIn) {
        this.shadowCornerRadius = shadowCornerRadiusIn;
        return (T) this;
    }

    /**
     * Set the color of the shadow of boom-button.
     *
     * @param shadowColorIn the shadow color
     * @return the builder
     */
    public T shadowColor(Color shadowColorIn) {
        this.shadowColor = shadowColorIn;
        return (T) this;
    }

    /**
     * Set the image drawable when boom-button is at normal-state.
     * <br><br>
     * <STRONG>Synchronicity:</STRONG> If the boom-button existed,
     * then synchronize this change to boom-button.
     *
     * @param normalImageDrawableIn the normal image drawable
     * @return the builder
     */
    public T normalImageDrawable(Element normalImageDrawableIn) {
        if (this.normalImageDrawable != normalImageDrawableIn) {
            this.normalImageDrawable = normalImageDrawableIn;
            BoomButton button = button();
            if (button != null) {
                button.normalImageDrawable = normalImageDrawableIn;
                button.updateImage();
            }
        }
        return (T) this;
    }

    /**
     * Set the image resource when boom-button is at normal-state.
     * <br><br>
     * <STRONG>Synchronicity:</STRONG> If the boom-button existed,
     * then synchronize this change to boom-button.
     *
     * @param normalImageResIn the normal image res
     * @return the builder
     */
    public T normalImageRes(int normalImageResIn) {
        if (this.normalImageRes != normalImageResIn) {
            this.normalImageRes = normalImageResIn;
            BoomButton button = button();
            if (button != null) {
                button.normalImageRes = normalImageResIn;
                button.updateImage();
            }
        }
        return (T) this;
    }

    /**
     * Set the image drawable when boom-button is at highlighted-state.
     * <br><br>
     * <STRONG>Synchronicity:</STRONG> If the boom-button existed,
     * then synchronize this change to boom-button.
     *
     * @param highlightedImageDrawableIn the highlighted image drawable
     * @return the builder
     */
    public T highlightedImageDrawable(Element highlightedImageDrawableIn) {
        if (this.highlightedImageDrawable != highlightedImageDrawableIn) {
            this.highlightedImageDrawable = highlightedImageDrawableIn;
            BoomButton button = button();
            if (button != null) {
                button.highlightedImageDrawable = highlightedImageDrawableIn;
                button.updateImage();
            }
        }
        return (T) this;
    }

    /**
     * Set the image resource when boom-button is at highlighted-state.
     * <br><br>
     * <STRONG>Synchronicity:</STRONG> If the boom-button existed,
     * then synchronize this change to boom-button.
     *
     * @param highlightedImageResIn the highlighted image res
     * @return the builder
     */
    public T highlightedImageRes(int highlightedImageResIn) {
        if (this.highlightedImageRes != highlightedImageResIn) {
            this.highlightedImageRes = highlightedImageResIn;
            BoomButton button = button();
            if (button != null) {
                button.highlightedImageRes = highlightedImageResIn;
                button.updateImage();
            }
        }
        return (T) this;
    }

    /**
     * Set the image drawable when boom-button is at unable-state.
     * <br><br>
     * <STRONG>Synchronicity:</STRONG> If the boom-button existed,
     * then synchronize this change to boom-button.
     *
     * @param unableImageDrawableIn the unable image drawable
     * @return the builder
     */
    public T unableImageDrawable(Element unableImageDrawableIn) {
        if (this.unableImageDrawable != unableImageDrawableIn) {
            this.unableImageDrawable = unableImageDrawableIn;
            BoomButton button = button();
            if (button != null) {
                button.unableImageDrawable = unableImageDrawableIn;
                button.updateImage();
            }
        }
        return (T) this;
    }

    /**
     * Set the image resource when boom-button is at unable-state.
     * <br><br>
     * <STRONG>Synchronicity:</STRONG> If the boom-button existed,
     * then synchronize this change to boom-button.
     *
     * @param unableImageResIn the unable image res
     * @return the builder
     */
    public T unableImageRes(int unableImageResIn) {
        if (this.unableImageRes != unableImageResIn) {
            this.unableImageRes = unableImageResIn;
            BoomButton button = button();
            if (button != null) {
                button.unableImageRes = unableImageResIn;
                button.updateImage();
            }
        }
        return (T) this;
    }

    /**
     * Set the rect of image.
     * By this method, you can set the position and size of the image-view in boom-button.
     * For example, builder.imageRect(new Rect(0, 50, 100, 100)) will make the
     * image-view's size to be 100 * 50 and margin-top to be 50 pixel.
     * <br><br>
     * <STRONG>Synchronicity:</STRONG> If the boom-button existed,
     * then synchronize this change to boom-button.
     *
     * @param imageRectIn the image rect, in pixel.
     * @return the builder
     */
    public T imageRect(Rect imageRectIn) {
        if (this.imageRect != imageRectIn) {
            this.imageRect = imageRectIn;
            BoomButton button = button();
            if (button != null) {
                button.imageRect = imageRectIn;
                button.updateImageRect();
            }
        }
        return (T) this;
    }

    /**
     * Set the padding of image.
     * By this method, you can control the padding in the image-view.
     * For instance, builder.imagePadding(new Rect(10, 10, 10, 10)) will make the
     * image-view content 10-pixel padding to itself.
     * <br><br>
     * <STRONG>Synchronicity:</STRONG> If the boom-button existed,
     * then synchronize this change to boom-button.
     *
     * @param imagePaddingIn the image padding
     * @return the builder
     */
    public T imagePadding(Rect imagePaddingIn) {
        if (this.imagePadding != imagePaddingIn) {
            this.imagePadding = imagePaddingIn;
            BoomButton button = button();
            if (button != null) {
                button.imagePadding = imagePaddingIn;
                button.updateImagePadding();
            }
        }
        return (T) this;
    }

    /**
     * Whether the boom-button should have a ripple effect.
     *
     * @param rippleEffectIn the ripple effect
     * @return the builder
     */
    public T rippleEffect(boolean rippleEffectIn) {
        this.rippleEffect = rippleEffectIn;
        return (T) this;
    }

    /**
     * The color of boom-button when it is at normal-state.
     * <br><br>
     * <STRONG>Synchronicity:</STRONG> If the boom-button existed,
     * then synchronize this change to boom-button.
     *
     * @param normalColorIn the normal color
     * @return the builder
     */
    public T normalColor(Color normalColorIn) {
        if (this.normalColor != normalColorIn) {
            this.normalColor = normalColorIn;
            BoomButton button = button();
            if (button != null) {
                button.normalColor = normalColorIn;
                button.updateButtonDrawable();
            }
        }
        return (T) this;
    }

    /**
     * The resource of color of boom-button when it is at normal-state.
     * <br><br>
     * <STRONG>Synchronicity:</STRONG> If the boom-button existed,
     * then synchronize this change to boom-button.
     *
     * @param normalColorResIn resource of normal color
     * @return the builder
     */
    public T normalColorRes(int normalColorResIn) {
        if (this.normalColorRes != normalColorResIn) {
            this.normalColorRes = normalColorResIn;
            BoomButton button = button();
            if (button != null) {
                button.normalColorRes = normalColorResIn;
                button.updateButtonDrawable();
            }
        }
        return (T) this;
    }

    /**
     * The color of boom-button when it is at highlighted-state.
     * <br><br>
     * <STRONG>Synchronicity:</STRONG> If the boom-button existed,
     * then synchronize this change to boom-button.
     *
     * @param highlightedColorIn the highlighted color
     * @return the builder
     */
    public T highlightedColor(Color highlightedColorIn) {
        if (this.highlightedColor != highlightedColorIn) {
            this.highlightedColor = highlightedColorIn;
            BoomButton button = button();
            if (button != null) {
                button.highlightedColor = highlightedColorIn;
                button.updateButtonDrawable();
            }
        }
        return (T) this;
    }

    /**
     * The resource of color of boom-button when it is at highlighted-state.
     * <br><br>
     * <STRONG>Synchronicity:</STRONG> If the boom-button existed,
     * then synchronize this change to boom-button.
     *
     * @param highlightedColorResIn resource of highlighted color
     * @return the builder
     */
    public T highlightedColorRes(int highlightedColorResIn) {
        if (this.highlightedColorRes != highlightedColorResIn) {
            this.highlightedColorRes = highlightedColorResIn;
            BoomButton button = button();
            if (button != null) {
                button.highlightedColorRes = highlightedColorResIn;
                button.updateButtonDrawable();
            }
        }
        return (T) this;
    }

    /**
     * The color of boom-button when it is at unable-state.
     * <br><br>
     * <STRONG>Synchronicity:</STRONG> If the boom-button existed,
     * then synchronize this change to boom-button.
     *
     * @param unableColorIn the unable color
     * @return the builder
     */
    public T unableColor(Color unableColorIn) {
        if (this.unableColor != unableColorIn) {
            this.unableColor = unableColorIn;
            BoomButton button = button();
            if (button != null) {
                button.unableColor = unableColorIn;
                button.updateButtonDrawable();
            }
        }
        return (T) this;
    }

    /**
     * The resource of color of boom-button when it is at unable-state.
     * <br><br>
     * <STRONG>Synchronicity:</STRONG> If the boom-button existed,
     * then synchronize this change to boom-button.
     *
     * @param unableColorResIn resource of unable color
     * @return the builder
     */
    public T unableColorRes(int unableColorResIn) {
        if (this.unableColorRes != unableColorResIn) {
            this.unableColorRes = unableColorResIn;
            BoomButton button = button();
            if (button != null) {
                button.unableColorRes = unableColorResIn;
                button.updateButtonDrawable();
            }
        }
        return (T) this;
    }

    /**
     * The color of boom-button when it is just a piece.
     * <br><br>
     * <STRONG>Synchronicity:</STRONG> If the boom-button existed,
     * then synchronize this change to boom-button.
     *
     * @param pieceColorIn color of piece
     * @return the builder
     */
    public T pieceColor(Color pieceColorIn) {
        if (this.pieceColor == null || this.pieceColor != pieceColorIn) {
            this.pieceColor = pieceColorIn;
            BoomButton button = button();
            if (button != null) button.pieceColor = pieceColorIn;
            if (piece != null) piece.setColor(pieceColorIn);
        }
        return (T) this;
    }

    /**
     * The resource of color of boom-button when it is just a piece.
     * <br><br>
     * <STRONG>Synchronicity:</STRONG> If the boom-button existed,
     * then synchronize this change to boom-button.
     *
     * @param pieceColorResIn resource of color of piece
     * @return the builder
     */
    public T pieceColorRes(int pieceColorResIn) {
        if (this.pieceColorRes != pieceColorResIn) {
            this.pieceColorRes = pieceColorResIn;
            BoomButton button = button();
            if (button != null) button.pieceColorRes = pieceColorResIn;
            if (piece != null) piece.setColorRes(pieceColorResIn);
        }
        return (T) this;
    }

    /**
     * Whether the boom-button is unable, default value is false.
     * <br><br>
     * <STRONG>Synchronicity:</STRONG> If the boom-button existed,
     * then synchronize this change to boom-button.
     *
     * @param unableIn the unable
     * @return the builder
     */
    public T unable(boolean unableIn) {
        if (this.unable != unableIn) {
            this.unable = unableIn;
            BoomButton button = button();
            if (button != null) {
                button.unable = unableIn;
                button.updateUnable();
                if (piece != null) piece.setColor(button.pieceColor());
            }
        }
        return (T) this;
    }
}
